import Watcher from "./observer/watcher";
import { callHook } from "./init";
import { createElement, createTextElement } from "./vdom/index";
import { patch } from "./vdom/patch";

export function lifeCycleMixin ( Vue ) {

  // 节点转虚拟dom _c('div',undefoined,[])
  Vue.prototype._c = function () {
    return createElement( this, ...arguments );
  };
  // 文本转虚拟dom _v(字符串 + name + 'xxx')
  Vue.prototype._v = function ( text ) {
    return createTextElement( this, text );
  };
  // 数据 _s(name)
  // 在模版里面访问对象，stringify会访问对象的每一个属性触发get做依赖收集
  Vue.prototype._s = function ( val ) {
    if ( typeof val === "object" ) return JSON.stringify( val );
    return val;
  };

  // 生成虚拟节点,并且在这个过程中，收集渲染Watcher
  Vue.prototype._render = function () {
    const vm = this
    const render = vm.$options.render
    const vnode = render.call( vm )
    return vnode
  }

  Vue.prototype._update = function ( vnode ) {
    let vm = this
    let preVnode = vm._vnode // 上一次的虚拟节点
    vm._vnode = vnode // 存储虚拟节点，用于下次更新使用
    if ( !preVnode ) {
      // 第一次渲染
      this.$el = patch( this.$el, vnode )
    } else {
      // 更新
      this.$el = patch( preVnode, vnode )
    }
  }
}

export function mountComponent ( vm ) {
  const updateComponent = () => {
    // 1.render函数产生虚拟节点
    // 2.根据虚拟节点产生真实节点
    // 3.在这个过程中，发生取值操作会收集渲染Watcher
    // 4.当数组发生变化后会触发更新
    vm._update( vm._render() );
  }

  // 创建Watcher，将渲染/更新 控制权交给Watcher
  new Watcher( vm,
    updateComponent,
    () => {
      callHook( vm, "beforeUpdate" );
    },
    { user: true } )
  callHook( vm, "mounted" );
}